package com.ts.api.http.receive.biz;

import com.alibaba.fastjson.JSONObject;
import com.gitee.apanlh.util.algorithm.digest.MD5;
import com.gitee.apanlh.util.base.ChooseEq;
import com.gitee.apanlh.util.base.Eq;
import com.gitee.apanlh.util.base.MapUtils;
import com.gitee.apanlh.util.cache.redis.RedisKeyBuilder;
import com.gitee.apanlh.util.cache.redis.api.RedisString;
import com.gitee.apanlh.util.dataformat.JsonUtils;
import com.gitee.apanlh.util.log.LogLevel;
import com.gitee.apanlh.util.net.http.HttpClientBuilder;
import com.gitee.apanlh.util.net.http.HttpResponse;
import com.gitee.apanlh.util.random.RandomUtils;
import com.gitee.apanlh.util.reflection.ClassConvertUtils;
import com.gitee.apanlh.util.reflection.CopyUtils;
import com.gitee.apanlh.util.valid.Assert;
import com.gitee.apanlh.util.valid.ValidParam;
import com.gitee.apanlh.web.http.HttpMethod;
import com.gitee.apanlh.web.model.vo.RequestVO;
import com.ts.api.common.constant.api.ApiEnum;
import com.ts.api.common.context.ApiContext;
import com.ts.api.common.entity.ApiParameter;
import com.ts.api.exp.ApiMsgException;
import com.ts.api.http.receive.entity.apply.req.ApiReceiveApplyFenLing;
import com.ts.api.http.receive.entity.dataexchange.req.ApiReceiveDataExchangeFenLing;
import com.ts.api.module.api.convert.core.annotation.ApiReceive;
import com.ts.api.module.api.convert.core.annotation.ApiReceiveConvertType;
import com.ts.api.module.api.convert.receive.entity.ApiReceiveApply;
import com.ts.api.module.api.convert.receive.entity.ApiReceiveDecrypt;
import com.ts.api.module.api.convert.receive.entity.ApiReceiveNotice;
import com.ts.api.module.api.convert.receive.entity.ApiReceivePhoneMd5DataExchange;
import com.ts.api.module.api.convert.receive.handler.ApiReceiveExistConvertHandler;
import com.ts.api.module.api.convert.receive.handler.ApiReceivePhoneMd5ConvertHandler;
import com.ts.api.module.api.entity.ApiUserRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Map;
import java.util.Objects;

/**
 * 纷领
 *
 * @author WJB
 */
@ApiReceive(apiClientId = "1936481818471452672",type = ApiReceiveConvertType.API_CHANNEL_PHONE_MD5)
@Slf4j
public class ApiReceivePhoneMd5FenLing implements ApiReceiveExistConvertHandler, ApiReceivePhoneMd5ConvertHandler {

    private static final String KEY = "HW8QP3NAllyhKKnaXQCv6w==";
    private static final String ALGORITHM_CIPHER_AES = "AES/ECB/PKCS5Padding";
    private static final String ALGORITHM_AES = "AES";
    private static final String CHARSET = "UTF-8";
    private static final String RESULT_URL = "https://loan.jycash.cn/clb/onlineLoan/dcr/api/notify/userAuthResult";

    private static final RedisString TEMP = RedisKeyBuilder.builder().withKeys("com.ts.api.http.receive.biz.ApiReceiveFenLing").buildString();

    private static String buildAESEncrypt(String data) {
        try {
            // 实例化Cipher对象,它用于完成实际的加密操作
            Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER_AES);
            // 还原密钥,并初始化Cipher对象,设置为加密模式
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(Base64.decodeBase64(ApiReceivePhoneMd5FenLing.KEY), ALGORITHM_AES));
            // 执行加密操作,加密后的结果通常都会用Base64编码进行传输
            // 将Base64中的URL非法字符如'+','/','='转为其他字符,详见RFC3548
            return Base64.encodeBase64URLSafeString(cipher.doFinal(data.getBytes(CHARSET)));
        } catch (Exception e) {
            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
        }
    }

    private static String buildAESDecrypt(String data) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER_AES);
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.decodeBase64(ApiReceivePhoneMd5FenLing.KEY), ALGORITHM_AES));
            return new String(cipher.doFinal(Base64.decodeBase64(data)), CHARSET);
        } catch (Exception e) {
            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
        }
    }

    @Override
    public Map<String, String> requireParam(RequestVO requestVO) throws Exception {
        //  获取请求体参数
        Map<String, Object> map = JsonUtils.toMap(requestVO.getBody());

        //  传递什么，验证什么Key
        String bizContent = String.valueOf(map.get("bizContent"));
        String apiName = String.valueOf(map.get("apiName"));
        String reqNo = String.valueOf(map.get("reqNo"));
        Assert.isNotEmptyThrows(reqNo, new ApiMsgException(ApiEnum.API_RECEIVE_BODY_MISS.format("reqNo")));
        Assert.isNotEmptyThrows(bizContent, new ApiMsgException(ApiEnum.API_RECEIVE_BODY_MISS.format("bizContent")));
        Assert.isNotEmptyThrows(apiName, new ApiMsgException(ApiEnum.API_RECEIVE_BODY_MISS.format("apiName")));

        //  返回需要的数据Map
        return MapUtils.newHashMap(newMap -> {
            newMap.put("bizContent", bizContent);
            newMap.put("apiName", apiName);
            newMap.put("reqNo", reqNo);
        });
    }

    @Override
    public ApiReceiveDecrypt decrypt(Map<String, String> paramMap, RequestVO requestVO) throws Exception {
        String reqNo = requestVO.getStr("reqNo");
        ApiParameter apiParameter = ApiContext.PARAMETER.get();
        apiParameter.put("reqNo", reqNo);

        String data = requestVO.getStr("bizContent");
        String decrypt = buildAESDecrypt(data);
        return new ApiReceiveDecrypt(decrypt);
    }

    @Override
    public ApiReceiveDecrypt decrypt(RequestVO requestVO) throws Exception {
        String reqNo = requestVO.getStr("reqNo");
        ApiParameter apiParameter = ApiContext.PARAMETER.get();
        apiParameter.put("reqNo", reqNo);

        String data = requestVO.getStr("bizContent");
        String decrypt = buildAESDecrypt(data);
        return new ApiReceiveDecrypt(decrypt);
    }

    @Override
    public ApiUserRequest parseDataExchange(ApiReceiveDecrypt apiReceiveDecrypt) throws Exception {
        ApiReceiveDataExchangeFenLing parse = apiReceiveDecrypt.parse(ApiReceiveDataExchangeFenLing.class);
        ApiUserRequest apiUserRequest = CopyUtils.copy(parse, ApiUserRequest.class);
        fieldMapping(apiUserRequest);
        String json = JsonUtils.toJson(apiUserRequest);
        TEMP.setForSeconds(parse.getPhoneMd5(), json, 300);
        return apiUserRequest;
    }

    @Override
    public Map<String, Object> respDataExchange(ApiReceivePhoneMd5DataExchange dataExchangeMessage) throws Exception {
        Map<String, Object> resultMap = MapUtils.newLinkedHashMap();

        boolean suc = dataExchangeMessage.hasBizSuc();
        if (!suc) {
            JSONObject noticeContent = new JSONObject();
            noticeContent.put("access", false);
            noticeContent.put("code", 0);
            noticeContent.put("failReason", dataExchangeMessage.getBizMsg());
            String data = buildAESEncrypt(noticeContent.toJSONString());

            resultMap.put("code", "0000");
            resultMap.put("message", "请求成功");
            resultMap.put("data", data);
            return resultMap;
        }

        JSONObject noticeContent = new JSONObject();
        noticeContent.put("access", true);
        noticeContent.put("code", 200);
        String data = buildAESEncrypt(noticeContent.toJSONString());

        resultMap.put("code", "0000");
        resultMap.put("message", "请求成功");
        resultMap.put("data", data);
        return resultMap;
    }

    @Override
    public ApiUserRequest parseApply(ApiReceiveDecrypt apiReceiveDecrypt) throws Exception {

        ApiReceiveApplyFenLing parse = apiReceiveDecrypt.parse(ApiReceiveApplyFenLing.class);
        ApiParameter apiParameter = ApiContext.PARAMETER.get();
        apiParameter.put("userId", parse.getUserId());
        apiParameter.put("applyNo", parse.getApplyNo());

        ApiUserRequest apiUserRequest = CopyUtils.copy(parse, ApiUserRequest.class);
        String phoneMd5 = MD5.create().digestToHex(parse.getPhone()).toLowerCase();
        String apiUserInfoStr = TEMP.get(phoneMd5, String.class);
        ApiUserRequest bean = JsonUtils.toBean(apiUserInfoStr, ApiUserRequest.class);
        apiUserRequest.setInsurancePolicy(bean.getInsurancePolicy());
        apiUserRequest.setLoanAmount(bean.getLoanAmount());
        apiUserRequest.setProvidentFund(bean.getProvidentFund());
        apiUserRequest.setRealEstateInfo(bean.getRealEstateInfo());
        apiUserRequest.setSesameCredit(bean.getSesameCredit());
        apiUserRequest.setSocialSecurity(bean.getSocialSecurity());
        apiUserRequest.setCarInfo(bean.getCarInfo());
        apiUserRequest.setAge(bean.getAge());
        // sex 性别（M男 F女）
        // sex 性别
        // 0 男
        // 1 女
        // 2 未知
        String sex = apiUserRequest.getSex();
        if (ValidParam.isNotEmpty(sex)) {
            String value = ChooseEq.create()
                    .when(sex, "M", "0")
                    .when(sex, "F", "1")
                    .end("0");
            apiUserRequest.setSex(value);
        }

        // 借款用途 1 生活消费 2 日常消费 3 家居装修 4 装修 5 学习健身 6 技能培训 7 电商购物 8 休闲旅游 9 购物分期 10 数码电器 11 租房分期
        // 1.旅游
        // 2.教育
        // 3.婚庆
        // 4.装修
        // 5.医疗
        // 6.个人消费
        String loanPurpose = apiUserRequest.getLoanPurpose();
        if (ValidParam.isNotEmpty(loanPurpose)) {
            String value = ChooseEq.create()
                    .when(loanPurpose, "1", "6")
                    .when(loanPurpose, "2", "2")
                    .when(loanPurpose, "3", "4")
                    .when(loanPurpose, "4", "4")
                    .when(loanPurpose, "5", "2")
                    .when(loanPurpose, "6", "2")
                    .when(loanPurpose, "7", "6")
                    .when(loanPurpose, "8", "1")
                    .when(loanPurpose, "9", "6")
                    .when(loanPurpose, "10", "6")
                    .when(loanPurpose, "11", "6")
                    .end("6");
            apiUserRequest.setLoanPurpose(value);
        }
        // 0：高中及以下； 1：大专 2：本科 3：硕士研究生 4：博士
        //  学历
        //  1.初中及以下
        //  2.中专
        //  3.高中
        //  4.大专
        //  5.本科
        //  6.研究生及以上
        String educationAttainment = apiUserRequest.getEducationAttainment();
        if (ValidParam.isNotEmpty(educationAttainment)) {
            String value = ChooseEq.create()
                    .when(educationAttainment, "0", "3")
                    .when(educationAttainment, "1", "4")
                    .when(educationAttainment, "2", "5")
                    .when(educationAttainment, "3", "6")
                    .when(educationAttainment, "4", "6")
                    .end("3");
            apiUserRequest.setEducationAttainment(value);
        }
        // 1：企事业单位工作人员 2：党政机关人员 3：军人 4：专业技术人员 5：商业、服务业工作人员 6：农林牧渔水利业生产及辅助人员 7：学生 8：其他
        // 职业
        // 1.上班族
        // 2.自由职业
        // 3.企业主（有营业执照）
        // 4.公务员或事业单位
        // 5.个体户
        String career = apiUserRequest.getCareer();
        if (ValidParam.isNotEmpty(career)) {
            String value = ChooseEq.create()
                    .when(career, "1", "4")
                    .when(career, "2", "4")
                    .when(career, "3", "4")
                    .when(career, "4", "1")
                    .when(career, "5", "1")
                    .when(career, "6", "1")
                    .when(career, "7", "5")
                    .when(career, "8", "5")
                    .end("5");
            apiUserRequest.setCareer(value);
        }

        String monthlySalary = apiUserRequest.getMonthlySalary();
        if (ValidParam.isNotEmpty(monthlySalary)) {
            String value = ChooseEq.create()
                    .when(monthlySalary, "1", "1")
                    .when(monthlySalary, "2", "2")
                    .when(monthlySalary, "3", "3")
                    .when(monthlySalary, "4", "4")
                    .when(monthlySalary, "4", "5")
                    .end("1");
            apiUserRequest.setMonthlySalary(value);
        }
        return apiUserRequest;
    }

    @Override
    public Map<String, Object> respApply(ApiReceiveApply applyMessage) throws Exception {
        Map<String, Object> resultMap = MapUtils.newLinkedHashMap();
        ApiParameter apiParameter = ApiContext.PARAMETER.get();

        boolean suc = applyMessage.hasBizSuc();
        String reqNo = apiParameter.get("reqNo");
        String userId = apiParameter.get("userId");
        String applyNo = apiParameter.get("applyNo");
        if (!suc) {
            JSONObject noticeContent = new JSONObject();
            noticeContent.put("applyStatus", "FAIL");
            noticeContent.put("outApplyNo", ApiContext.REQ_ID.get());
            noticeContent.put("failReason", applyMessage.getBizMsg());
            String data = buildAESEncrypt(noticeContent.toJSONString());

            resultMap.put("code", "0000");
            resultMap.put("message", "返回成功");
            resultMap.put("data", data);
            resultMap.put("reqNo", reqNo);
            resultMap.put("respNo", ApiContext.REQ_ID.get());

            sendResult(userId, "FAIL", reqNo);
            return resultMap;
        }

        JSONObject noticeContent = new JSONObject();
        noticeContent.put("applyStatus", "APPLYING");
        noticeContent.put("outApplyNo", ApiContext.REQ_ID.get());
        String data = buildAESEncrypt(noticeContent.toJSONString());
        //加密完成后添加额外需要保存到redis的值
        noticeContent.put("reqNo", reqNo);
        noticeContent.put("respNo", ApiContext.REQ_ID.get());
        noticeContent.put("userId", userId);

        resultMap.put("code", "0000");
        resultMap.put("message", "返回成功:日志信息" + applyMessage.getBizMsg());
        resultMap.put("data", data);
        resultMap.put("reqNo", reqNo);
        resultMap.put("respNo", ApiContext.REQ_ID.get());
        // 保存成功的信息，方便后续授信查询
        TEMP.setForMinutes(applyNo, noticeContent.toString(), 35);
        sendResult(userId, "SUCCESS", applyNo);
        return resultMap;
    }

    private static void sendResult(String userId, String creditResult, String applyNo) {
        //异步任务，通知主动通知对方进件结果
        JSONObject sendContent = new JSONObject();
        sendContent.put("applyNo", applyNo);
        sendContent.put("userId", userId);
        sendContent.put("creditResult", creditResult);
        String data = buildAESEncrypt(sendContent.toJSONString());
        Map<String, Object> sendMap = MapUtils.newLinkedHashMap();
        sendMap.put("activityCode", "userAuthResult");
        sendMap.put("parameters", data);
        HttpResponse build = HttpClientBuilder.builder(RESULT_URL, HttpMethod.POST)
                .withBodyJson(sendMap)
                .withHeader((headerMap) -> headerMap.put("Content-Type", "application/json"))
                .withLogLevel(LogLevel.WARN)
                .withErrorLogLevel(LogLevel.WARN)
                .build();
        String respStr = build.getStr();
        JSONObject jsonObject = JsonUtils.get(respStr);
        String code = jsonObject.getString("code");
        if (!Eq.object(code, "0000")) {
            throw new RuntimeException("通知进件结果发送失败:" + respStr);
        }
    }

    @Override
    public void fieldMapping(ApiUserRequest apiUserRequest) {

        //  随机选择1个有的,选择出来剩下的随机有还是无
        // 定义字段数组
        String[] fields = {
                "CAR",
                "REAL_ESTATE",
                "INSURANCE",
                "SOCIAL_SECURITY"
        };

        // 初始化所有字段为1
        for (String field : fields) {
            setFieldValue(apiUserRequest, field, "1");
        }
        // 随机选择1个唯一索引
        int count = 0;
        while (count < 1) {
            int index = RandomUtils.randomInt(0, fields.length - 1);
            // 检查是否已设置为1
            if ("1".equals(getFieldValue(apiUserRequest, fields[index]))) {
                setFieldValue(apiUserRequest, fields[index], null);
                count++;
            }
        }
//        // 车
//        apiUserInfo.setCarInfo(ClassConvertUtils.toStr(RandomUtils.randomInt(1, 4)));
//        // 房
//        apiUserInfo.setRealEstateInfo(ClassConvertUtils.toStr(RandomUtils.randomInt(1, 6)));
//        // 保单
//        apiUserInfo.setInsurancePolicy(ClassConvertUtils.toStr(RandomUtils.randomInt(1, 4)));
//        // 社保
//        apiUserInfo.setSocialSecurity(ClassConvertUtils.toStr(RandomUtils.randomInt(1, 4)));
        // 公积金
        apiUserRequest.setProvidentFund("4");
        // 芝麻分
        apiUserRequest.setSesameCredit(ClassConvertUtils.toStr(RandomUtils.randomInt(1, 4)));
        // 贷款金额
        apiUserRequest.setLoanAmount(ClassConvertUtils.toStr(RandomUtils.randomInt(1, 4)));
        // 逾期
        apiUserRequest.setOverdue("1");
    }

    @Override
    public Object reqPullApplyNotice(ApiReceiveNotice apiReceiveNotice) {
        return null;
    }

    @Override
    public boolean respPullApplyNotice(String responseBody) {
        return false;
    }

    @Override
    public Object reqPushApplyNotice(ApiReceiveNotice apiReceiveNotice) {
        return null;
    }

    @Override
    public boolean respPushApplyNotice(String responseBody) {
        return false;
    }

    // 获取字段值（用于验证）
    private String getFieldValue(ApiUserRequest apiUserRequest, String field) {
        return switch (field) {
            case "CAR" -> apiUserRequest.getCarInfo();
            case "REAL_ESTATE" -> apiUserRequest.getRealEstateInfo();
            case "PROVIDENT_FUND" -> apiUserRequest.getProvidentFund();
            case "INSURANCE" -> apiUserRequest.getInsurancePolicy();
            case "SOCIAL_SECURITY" -> apiUserRequest.getSocialSecurity();
            default -> "0";
        };
    }

    // 设置字段值
    private void setFieldValue(ApiUserRequest apiUserRequest, String field, String value) {
        if ("1".equals(value)) {
            switch (field) {
                case "CAR":
                    apiUserRequest.setCarInfo(value);
                    break;
                case "REAL_ESTATE":
                    apiUserRequest.setRealEstateInfo(value);
                    break;
                case "PROVIDENT_FUND":
                    apiUserRequest.setProvidentFund(value);
                    break;
                case "INSURANCE":
                    apiUserRequest.setInsurancePolicy(value);
                    break;
                case "SOCIAL_SECURITY":
                    apiUserRequest.setSocialSecurity(value);
                    break;
            }
        } else {
            switch (field) {
                case "CAR":
                    apiUserRequest.setCarInfo(ClassConvertUtils.toStr(RandomUtils.randomInt(2, 4)));
                    break;
                case "REAL_ESTATE":
                    apiUserRequest.setRealEstateInfo(ClassConvertUtils.toStr(RandomUtils.randomInt(2, 6)));
                    break;
                case "PROVIDENT_FUND":
                    apiUserRequest.setProvidentFund(ClassConvertUtils.toStr(RandomUtils.randomInt(2, 4)));
                    break;
                case "INSURANCE":
                    apiUserRequest.setInsurancePolicy(ClassConvertUtils.toStr(RandomUtils.randomInt(2, 4)));
                    break;
                case "SOCIAL_SECURITY":
                    apiUserRequest.setSocialSecurity(ClassConvertUtils.toStr(RandomUtils.randomInt(2, 4)));
                    break;
            }
        }
    }

    public Map<String, Object> result(String body) {
        JSONObject jsonObject = JsonUtils.get(body);
        String reqNo = jsonObject.getString("reqNo");
        String content = buildAESDecrypt(jsonObject.getString("bizContent"));
        JSONObject contentObject = JsonUtils.get(content);
        String contentUserId = contentObject.getString("userId");
        String applyNo = contentObject.getString("applyNo");

        // 对方二次请求判断是否已成功分发
        String reqNoInfo = TEMP.get(applyNo, String.class);
        JSONObject reqNoInfoJsonObject = JsonUtils.get(reqNoInfo);

        if (Objects.isNull(reqNoInfoJsonObject)) {
            JSONObject noticeContent = new JSONObject();
            noticeContent.put("applyNo", applyNo);
            noticeContent.put("userId", contentUserId);
            noticeContent.put("creditResult", "FAIL");
            noticeContent.put("failReason", "查询授信结果失败");
            String data = buildAESEncrypt(noticeContent.toJSONString());

            Map<String, Object> resultMap = MapUtils.newLinkedHashMap();
            resultMap.put("code", "0000");
            resultMap.put("message", "返回成功:查询授信结果");
            resultMap.put("data", data);
            resultMap.put("reqNo", reqNo);
            log.error("查询授信结果失败:已超过35分钟");
            return resultMap;
        }

        String userId = reqNoInfoJsonObject.getString("userId");
        String respNo = reqNoInfoJsonObject.getString("respNo");

        // 校验进件和查询时带过来的userid是否一致
        if (Eq.str(contentUserId, userId)) {
            JSONObject noticeContent = new JSONObject();
            noticeContent.put("applyNo", applyNo);
            noticeContent.put("userId", contentUserId);
            noticeContent.put("creditResult", "SUCCESS");
            String data = buildAESEncrypt(noticeContent.toJSONString());

            Map<String, Object> resultMap = MapUtils.newLinkedHashMap();
            resultMap.put("code", "0000");
            resultMap.put("message", "返回成功：查询授信结果");
            resultMap.put("data", data);
            resultMap.put("reqNo", reqNo);
            resultMap.put("respNo", respNo);
            log.info("查询授信结果成功");
            return resultMap;
        }

        JSONObject noticeContent = new JSONObject();
        noticeContent.put("creditResult", "FAIL");
        noticeContent.put("failReason", "查询授信结果失败");
        noticeContent.put("applyNo", applyNo);
        noticeContent.put("userId", contentUserId);
        String data = buildAESEncrypt(noticeContent.toJSONString());

        Map<String, Object> resultMap = MapUtils.newLinkedHashMap();
        resultMap.put("code", "0000");
        resultMap.put("message", "返回成功:查询授信结果");
        resultMap.put("data", data);
        resultMap.put("reqNo", reqNo);
        resultMap.put("respNo", respNo);
        log.error("查询授信结果失败:UserId返回不一致");

        return resultMap;
    }
}
